package com.bootdo.common.utils.encryptionUtil;

import java.math.BigInteger;

public class Seal {
    public final static int WORDS_PER_SEAL_CALL = 512;
    public final static int BUFFER_SIZE = 500;
    public final static int ALG_OK = 0;
    public final static int ALG_NOTOK = 1;


    public class seal_ctx {
        longarray t;
        longarray s;
        longarray r;
        long counter;
        longarray ks_buf;
        int ks_pos;

        public seal_ctx() {
            t = new longarray(520);
            s = new longarray(265);
            r = new longarray(20);
            ks_buf = new longarray(WORDS_PER_SEAL_CALL);

            counter = 0;
            ks_pos = 0;
        }
    }

    public class longarray {
        public long a[];

        public longarray(int n) {
            a = new long[n];
        }

        public void clear() {
            int i;
            for (i = 0; i < a.length; i++)
                a[i] = 0;
        }
    }

    public class intarray {
        public int a[];

        intarray(int n) {
            a = new int[n];
        }

        public void clear() {
            int i;
            for (i = 0; i < a.length; i++)
                a[i] = 0;
        }
    }

    public class bytearray {
        public byte a[];

        public bytearray(int n) {
            a = new byte[n];
        }

        public void clear() {
            int i;
            for (i = 0; i < a.length; i++)
                a[i] = 0;
        }
    }

    public long ROT2(long x) {
        return ((x >>> 2) | x << 30) & 0x0FFFFFFFFl;
    }

    public long ROT8(long in) {
        return ((in >>> 8) | in << 24) & 0x0FFFFFFFFl;
    }

    public long ROT9(long x) {
        return ((x >>> 9) | x << 23) & 0x0FFFFFFFFl;
    }

    public long ROT16(long x) {
        return ((x >>> 16) | x << 16) & 0x0FFFFFFFFl;
    }

    public long ROT24(long x) {
        return ((x >>> 24) | x << 8) & 0x0FFFFFFFFl;
    }

    public long ROT27(long a) {
        return ((a >>> 27) | a << 5) & 0x0FFFFFFFFl;
    }

    public long F10(long x, long y, long z) {
        return (x & y | (~x) & z) & 0x0FFFFFFFFl;
    }

    public long F2(long x, long y, long z) {
        return (x ^ y ^ z) & 0x0FFFFFFFFl;
    }

    public long F3(long x, long y, long z) {
        return ((x & y) | (x & z) | (y & z)) & 0x0FFFFFFFFl;
    }

    public long F4(long x, long y, long z) {
        return (x ^ y ^ z) & 0x0FFFFFFFFl;
    }

    public long WORD(bytearray in, int idx) {
        int i0 = in.a[idx] & 0xFF;
        int i1 = in.a[idx + 1] & 0xFF;
        int i2 = in.a[idx + 2] & 0xFF;
        int i3 = in.a[idx + 3] & 0xFF;


        return ((i0 << 24) | (i1 << 16) | (i2 << 8) | i3) & 0x0FFFFFFFFl;
    }

    public int g(bytearray in, int i, longarray h, int index) {
        long h0, h1, h2, h3, h4, a, b, c, d, e;
        long w[] = new long[80];
        long temp;

        int idx = 0;

        h0 = WORD(in, idx);
        idx += 4;
        h1 = WORD(in, idx);
        idx += 4;
        h2 = WORD(in, idx);
        idx += 4;
        h3 = WORD(in, idx);
        idx += 4;
        h4 = WORD(in, idx);

        w[0] = i;
        for (i = 1; i < 16; i++)
            w[i] = 0;

        for (i = 16; i < 80; i++)
            w[i] = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];

        a = h0;
        b = h1;
        c = h2;
        d = h3;
        e = h4;

        for (i = 0; i < 20; i++) {
            temp = (ROT27(a) + F10(b, c, d) + e + w[i] + 0x5a827999) & 0x0FFFFFFFFl;
            long l27 = ROT27(a);
            long f10 = F10(b, c, d);
            long wi = w[i];

            e = d;
            d = c;
            c = ROT2(b);
            b = a;
            a = temp;
        }

        for (i = 20; i < 40; i++) {
            temp = (ROT27(a) + F2(b, c, d) + e + w[i] + 0x6ed9eba1) & 0x0FFFFFFFFl;
            e = d;
            d = c;
            c = ROT2(b);
            b = a;
            a = temp;
        }

        for (i = 40; i < 60; i++) {
            temp = (ROT27(a) + F3(b, c, d) + e + w[i] + 0x8f1bbcdc) & 0x0FFFFFFFFl;
            e = d;
            d = c;
            c = ROT2(b);
            b = a;
            a = temp;
        }

        for (i = 60; i < 80; i++) {
            temp = (ROT27(a) + F4(b, c, d) + e + w[i] + 0xca62c1d6) & 0x0FFFFFFFFl;
            e = d;
            d = c;
            c = ROT2(b);
            b = a;
            a = temp;
        }

        h.a[index] = (h0 + a) & 0x0FFFFFFFFl;
        h.a[index + 1] = (h1 + b) & 0x0FFFFFFFFl;
        h.a[index + 2] = (h2 + c) & 0x0FFFFFFFFl;
        h.a[index + 3] = (h3 + d) & 0x0FFFFFFFFl;
        h.a[index + 4] = (h4 + e) & 0x0FFFFFFFFl;

        return ALG_OK;
    }

    public int seal_init(seal_ctx c, bytearray key) {
        int i;
        longarray h = new longarray(5);

        for (i = 0; i < 510; i += 5)
            g(key, i / 5, c.t, i);
        g(key, 510 / 5, h, 0);

        for (i = 510; i < 512; i++)
            c.t.a[i] = h.a[i - 510];
        g(key, (-1 + 0x1000) / 5, h, 0);

        for (i = 0; i < 4; i++)
            c.s.a[i] = h.a[i + 1];

        for (i = 4; i < 254; i += 5)
            g(key, (i + 0x1000) / 5, c.s, i);
        g(key, (254 + 0x1000) / 5, h, 0);

        for (i = 254; i < 256; i++)
            c.s.a[i] = h.a[i - 254];
        g(key, (-2 + 0x2000) / 5, h, 0);

        for (i = 0; i < 3; i++)
            c.r.a[i] = h.a[i + 2];

        for (i = 3; i < 13; i += 5)
            g(key, (i + 0x2000) / 5, c.r, i);
        g(key, (13 + 0x2000) / 5, h, 0);

        for (i = 13; i < 16; i++)
            c.r.a[i] = h.a[i - 13];

        return ALG_OK;
    }

    public int seal(seal_ctx key, long in, longarray out) {
        short i, j, l;
        long a = 0, b = 0, c = 0, d = 0, n1, n2, n3, n4;
        int p, q;
        int index = 0;

        for (l = 0; l < 4; l++) {
            a = in ^ (key.r.a[4 * l]);
            b = ROT8(in) ^ (key.r.a[4 * l + 1]);
            c = ROT16(in) ^ (key.r.a[4 * l + 2]);
            d = ROT24(in) ^ (key.r.a[4 * l + 3]);
        }

        for (j = 0; j < 2; j++) {
            p = (int) (a & 0x7fc);
            b += key.t.a[p / 4];
            b &= 0x0FFFFFFFFl;
            a = ROT9(a);

            p = (int) (b & 0x7fc);
            c += key.t.a[p / 4];
            c &= 0x0FFFFFFFFl;
            b = ROT9(b);

            p = (int) (c & 0x7fc);
            d += key.t.a[p / 4];
            d &= 0x0FFFFFFFFl;
            c = ROT9(c);

            p = (int) (d & 0x7fc);
            a += key.t.a[p / 4];
            a &= 0x0FFFFFFFFl;
            d = ROT9(d);

            n1 = d;
            n2 = b;
            n3 = a;
            n4 = c;

            p = (int) (a & 0x7fc);
            b += key.t.a[p / 4];
            b &= 0x0FFFFFFFFl;
            a = ROT9(a);

            p = (int) (b & 0x7fc);
            c += key.t.a[p / 4];
            c &= 0x0FFFFFFFFl;
            b = ROT9(b);

            p = (int) (c & 0x7fc);
            d += key.t.a[p / 4];
            d &= 0x0FFFFFFFFl;
            c = ROT9(c);

            p = (int) (d & 0x7fc);
            a += key.t.a[p / 4];
            a &= 0x0FFFFFFFFl;
            d = ROT9(d);

            for (i = 0; i < 64; i++) {
                p = (int) (a & 0x7fc);
                b += key.t.a[p / 4];
                b &= 0x0FFFFFFFFl;
                a = ROT9(a);
                b ^= a;

                q = (int) (b & 0x7fc);
                c ^= key.t.a[q / 4];
                b = ROT9(b);
                c += b;
                c &= 0x0FFFFFFFFl;

                p = (int) ((p + c) & 0x7fc);
                d += key.t.a[p / 4];
                d &= 0x0FFFFFFFFl;
                c = ROT9(c);
                d ^= c;

                q = (int) ((q + d) & 0x7fc);
                a ^= key.t.a[q / 4];
                d = ROT9(d);
                a += d;
                a &= 0x0FFFFFFFFl;

                p = (int) ((p + a) & 0x7fc);
                b ^= key.t.a[p / 4];
                b &= 0x0FFFFFFFFl;
                a = ROT9(a);

                q = (int) ((q + b) & 0x7fc);
                c += key.t.a[q / 4];
                c &= 0x0FFFFFFFFl;
                b = ROT9(b);

                p = (int) ((p + c) & 0x7fc);
                d ^= key.t.a[p / 4];
                d &= 0x0FFFFFFFFl;
                c = ROT9(c);

                q = (int) ((q + d) & 0x7fc);
                a += key.t.a[q / 4];
                a &= 0x0FFFFFFFFl;
                d = ROT9(d);

                out.a[index] = (b + key.s.a[4 * i]) & 0x0FFFFFFFFl;
                out.a[index + 1] = (c ^ key.s.a[4 * i + 1]) & 0x0FFFFFFFFl;
                out.a[index + 2] = (d + key.s.a[4 * i + 2]) & 0x0FFFFFFFFl;
                out.a[index + 3] = (a ^ key.s.a[4 * i + 3]) & 0x0FFFFFFFFl;
                index += 4;

                if ((i & 1) != 0) {
                    a = (a + n3) & 0x0FFFFFFFFl;
                    c = (c + n4) & 0x0FFFFFFFFl;
                } else {
                    a = (a + n1) & 0x0FFFFFFFFl;
                    c = (c + n2) & 0x0FFFFFFFFl;
                }
            }
        }

        return ALG_OK;
    }

    public void seal_refill_buffer(seal_ctx c) {
        seal(c, c.counter, c.ks_buf);
        c.counter++;
        c.ks_pos = 0;
    }

    public void seal_key(seal_ctx c, bytearray key) {
        seal_init(c, key);
        c.counter = 0;
        c.ks_pos = WORDS_PER_SEAL_CALL;
    }

    public void seal_encrypt(seal_ctx c, bytearray data_ptr, long w, long nPos) {
//		for (int i = 0; i < w; i++) {
//			if (c.ks_pos >= WORDS_PER_SEAL_CALL)
//				seal_refill_buffer(c);
//			data_ptr.a[i] ^= c.ks_buf.a[c.ks_pos];
//			c.ks_pos++;
//		}

        long count = nPos / WORDS_PER_SEAL_CALL;
        long pos = nPos % WORDS_PER_SEAL_CALL;
        c.counter = count;
        seal_refill_buffer(c);
        c.ks_pos = (int) pos;

        for (int i = 0; i < w; i++) {
            if (c.ks_pos >= WORDS_PER_SEAL_CALL)
                seal_refill_buffer(c);


            //data_ptr.a[i] ^= c.ks_buf.a[c.ks_pos];

            long b0 = c.ks_buf.a[c.ks_pos] & 0x000000FF;
            long b1 = (c.ks_buf.a[c.ks_pos] & 0x0000FF00) >>> 8;
            long b2 = (c.ks_buf.a[c.ks_pos] & 0x00FF0000) >>> 16;
            long b3 = (c.ks_buf.a[c.ks_pos] & 0xFF000000) >>> 24;

            data_ptr.a[i] ^= b0;
            data_ptr.a[i] ^= b1;
            data_ptr.a[i] ^= b2;
            data_ptr.a[i] ^= b3;

            c.ks_pos++;
        }
    }

    public void seal_decrypt(seal_ctx c, bytearray data_ptr, long w, long nPos) {
        seal_encrypt(c, data_ptr, w, nPos);
    }

    public void seal_resynch(seal_ctx c, int synch_word) {
        c.counter = synch_word;
        c.ks_pos = WORDS_PER_SEAL_CALL;
    }

    /**
     * 2020.03.25 未来修改
     * 从C++代码中修改而来
     */
    void InitEncryptKey() {
        Seal seal = new Seal();
        seal_ctx sc = seal.new seal_ctx();

        bytearray buf = seal.new bytearray(1000);
        buf.a[0] = 'a';
        buf.a[1] = 'a';
        buf.a[2] = 'b';
        buf.a[3] = 'b';
        buf.a[4] = 'c';
        buf.a[5] = 'c';
        buf.a[6] = 'd';
        buf.a[7] = 'd';

        byte[] result = Sha.ShaEncrypt("YANGTUFS", 0);
        bytearray key = seal.new bytearray(20);
        for (int i = 0; i < 20; i++) {
            key.a[i] = result[i];
        }

        seal_key(sc, key);
    }

    public static void InitEncryptKey(Seal seal, seal_ctx sc) {
        bytearray buf = seal.new bytearray(1000);
        buf.a[0] = 'a';
        buf.a[1] = 'a';
        buf.a[2] = 'b';
        buf.a[3] = 'b';
        buf.a[4] = 'c';
        buf.a[5] = 'c';
        buf.a[6] = 'd';
        buf.a[7] = 'd';

        byte[] result = Sha.ShaEncrypt("YANGTUFS", 0);
        bytearray key = seal.new bytearray(20);
        for (int i = 0; i < 20; i++) {
            key.a[i] = result[i];
        }
        seal.seal_key(sc, key);
    }


}
